package com.vt.common.security.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.vt.admin.api.dto.user.redis.UserOrganization;
import com.vt.admin.api.dto.user.redis.UserPosition;
import com.vt.common.core.constant.CommonConstant;
import com.vt.common.security.util.enums.DataScopeType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 数据区隔工具类
 *
 * @Auther: PanZha
 * @Date: 2018/11/1 17:27
 */
@Component
public class DataScopeUtil {

    @Autowired
    StringRedisTemplate redisTemplate;


    /**
     * @return 下级用户ID
     */
    public Set<String> scope() {
        return scope(DataScopeType.PEOPLE);
    }

    /**
     * @return 下级用户ID
     */
    public Set<String> scope(DataScopeType type, String... userId) {
        if (null == userId
                || userId.length == 0) {
            userId = new String[]{SecurityUtils.getUser().getId()};
        }

        List<String> users = Arrays.asList(userId);
        Set<String> set;
        switch (type) {
            case COMPANY_MANAGER:
                set = scopeByCompanyType(users, type);
                break;
            case COMPANY_BRANCH:
                set = scopeByCompanyType(users, type);
                break;
            case PEOPLE:
                set = scopeByPeople(users);
                break;
            default:
                set = new HashSet();
                break;
        }
        set.addAll(users);
        return set;
    }

    private Set<String> scopeByPeople(Collection<String> users) {
        List<UserOrganization> all = getOrgan();

        //children organization user
        Set<String> set = getUserIdByChs(all,
                getChildrenNode(byUser(all, users), all));

        //children position user
        set.addAll(getUserIdByChPosition(all, users));
        return set;
    }

    private Collection<? extends String> getUserIdByChPosition(Collection<UserOrganization> allOrganization, Collection<String> users) {
        // 同级组织的 user
        Set<String> sameUsers = getUserIdByChs(allOrganization,
                idxByUser(allOrganization, users));

        if (sameUsers.size() <= 1)
            return new HashSet();

        List<UserPosition> poss = getPosition(sameUsers);
        List<UserPosition> root = getPosition(users, poss);

        // 同级组织中下级职位的 user
        return getUserIdByChsPosition(poss, root);
    }

    private Set<String> getUserIdByChsPosition(Collection<UserPosition> all, Collection<UserPosition> roots) {
        Set<String> set = new HashSet();
        for (UserPosition up : roots) {
            searchChsPosition(up, all, set);
        }
        return set;
    }

    private void searchChsPosition(UserPosition up, Collection<UserPosition> all, Set<String> set) {
        for (UserPosition obj : all) {
            if (StrUtil.equals(up.getPositionId(), obj.getParentId())) {
                set.add(obj.getUserId());
                searchChsPosition(obj, all, set);
            }
        }
    }

    private Set<String> scopeByCompanyType(List<String> users, DataScopeType scopeType) {
        List<UserOrganization> all = getOrgan();
        return getUserIdByChs(all, getChildrenByUser(all, users, scopeType));
    }

    /**
     * search user by children organization
     *
     * @return user idx set
     */
    private Set<String> getUserIdByChs(Collection<UserOrganization> all, Collection<String> chs) {
        Set<String> set = new HashSet();
        for (UserOrganization uo : all) {
            if (chs.contains(uo.getOrganizationId()))
                set.add(uo.getUserId());
        }
        return set;
    }

    private Set<String> getChildrenByUser(Collection<UserOrganization> all, Collection<String> users, DataScopeType scopeType) {
        return getChildrenNode(getRootNode(all, byUser(all, users), scopeType), all);
    }

    protected Set<String> getChildrenNode(Collection<UserOrganization> roots, Collection<UserOrganization> all) {
        Set<String> rs = new HashSet();
        for (UserOrganization uo : roots) {
            searchChildrenNode(uo, all, rs);
        }
        return rs;
    }

    private void searchChildrenNode(UserOrganization uo, Collection<UserOrganization> all, Collection<String> rs) {
        for (UserOrganization obj : all) {
            if (StrUtil.equals(obj.getParentId(), uo.getOrganizationId())) {
                rs.add(obj.getOrganizationId());
                searchChildrenNode(obj, all, rs);
            }
        }
    }

    private Set<UserOrganization> getRootNode(Collection<UserOrganization> all, Collection<UserOrganization> subList, DataScopeType scopeType) {
        Set<UserOrganization> roots = new HashSet();
        for (UserOrganization uo : subList) {
            searchRootNode(all, uo, scopeType, roots);
        }

        if (roots.size() == 0
                && scopeType == DataScopeType.COMPANY_BRANCH) {
            //没有上级分公司，用管理公司权限
            roots = getRootNode(all, subList, DataScopeType.COMPANY_MANAGER);
        }
        return roots;
    }

    protected void searchRootNode(Collection<UserOrganization> all, UserOrganization uo, DataScopeType scopeType, Collection<UserOrganization> roots) {
        for (UserOrganization obj : all) {
            if (StrUtil.equals(obj.getOrganizationId(),
                    uo.getParentId())) {
                if (StrUtil.isNotEmpty(obj.getType()) &&
                        Long.valueOf(obj.getType()) <= scopeType.code) {
                    roots.add(obj);
                    return;
                }
                searchRootNode(all, obj, scopeType, roots);
            }
        }
    }

    protected List<UserOrganization> getOrgan() {
        return JSONUtil.toList(JSONUtil.parseArray(
                redisTemplate.opsForValue().get(CommonConstant.REDIS_ORGANIZATION_PREFIX))
                , UserOrganization.class);
    }

    private List<UserPosition> getPosition() {
        return JSONUtil.toList(JSONUtil.parseArray(
                redisTemplate.opsForValue().get(CommonConstant.REDIS_POSITION_PREFIX))
                , UserPosition.class);
    }

    private List<UserPosition> getPosition(Collection<String> users, Collection<UserPosition> poss) {
        List<UserPosition> list = new ArrayList(users.size());
        for (UserPosition up : poss) {
            if (users.contains(up.getUserId()))
                list.add(up);
        }
        return list;
    }

    private List<UserPosition> getPosition(Collection<String> users) {
        return getPosition(users, getPosition());
    }

    protected List<UserOrganization> byUser(Collection<UserOrganization> all, Collection<String> users) {
        List<UserOrganization> subList = new ArrayList();

        for (UserOrganization organ : all) {
            if (users.contains(organ.getUserId())) {
                subList.add(organ);
            }
        }
        return subList;
    }

    private Set<String> idxByUser(Collection<UserOrganization> all, Collection<String> users) {
        Set<String> set = new HashSet();

        for (UserOrganization organ : all) {
            if (users.contains(organ.getUserId())) {
                set.add(organ.getOrganizationId());
            }
        }
        return set;
    }
}
